﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace VisualWix
{
    public partial class Wix
    {
        private List<WixFragment> fragments = new List<WixFragment>();
        private List<WixProductFeatureComponentGroupRef> cmpGroupReferences = new List<WixProductFeatureComponentGroupRef>();

        [field: NonSerialized]
        private Uri wxsFilepath;

        public void Initialize()
        {
            this.Product = new WixProduct();
            this.Product.Initialize();
        }

        public void SetWxsFilePath(string filepath)
        {
            wxsFilepath = new Uri(filepath);
        }

        public Uri GetWxsFilepath()
        {
            return wxsFilepath;
        }

        public void UpdateProductDetails(string name, string version, string manufacturer)
        {
            this.Product.Manufacturer = manufacturer;
            this.Product.Name = name;
            this.Product.Version = version;
        }

        public void UpdatePackageDetails(string description, string installScope, string platform, string installPrivileges)
        {
            this.Product.Package.InstallScope = installScope;
            this.Product.Package.Platform = platform;
            this.Product.Package.InstallPrivileges = installPrivileges;
            this.Product.Package.Description = description;
        }

        public void UpdateDirectoryStructure(DirectoryItem targetDirectory, DirectoryItem shortcutsDirectory)
        {
            AddFilesAndFolders(targetDirectory);
            AddShortcuts(shortcutsDirectory);

            this.Product.Feature.ComponentGroupRef = cmpGroupReferences.ToArray();
            this.Fragment = fragments.ToArray();
        }

        public void UpdateProperties(List<WixProductProperty> properties)
        {
            if (properties != null && properties.Count > 0)
            {
                this.Product.Property = properties.ToArray();
            }
        }

        public void GetDirectoryStructure(DirectoryItem targetDir, DirectoryItem shortcutDir)
        {
            List<Item> fileDirectories = new List<Item>();
            List<Item> shortcutDirectories = new List<Item>();

            DirectoryItem directories = GetDirectoryItems();

            foreach (DirectoryItem nextDirectory in directories.Items)
            {
                if (nextDirectory.HierarchyType == HierarchyType.SystemShortcutFolder)
                {
                    shortcutDirectories.Add(nextDirectory);
                    nextDirectory.ParentItem = shortcutDir;
                }
                else
                {
                    fileDirectories.Add(nextDirectory);
                    nextDirectory.ParentItem = targetDir;
                }
            }

            targetDir.Items = new System.Collections.ObjectModel.ObservableCollection<Item>(fileDirectories);
            shortcutDir.Items = new System.Collections.ObjectModel.ObservableCollection<Item>(shortcutDirectories);
        }

        public static Wix LoadFromFile(string fileName)
        {
            Wix wixDetails = null;
            if (!string.IsNullOrWhiteSpace(fileName) && System.IO.File.Exists(fileName))
            {
                try
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(Wix));
                    XmlReader reader = XmlReader.Create(fileName);

                    wixDetails = serializer.Deserialize(reader) as Wix;
                    wixDetails.SetWxsFilePath(fileName);
                    reader.Close();
                }
                catch (Exception ex)
                {
                    System.Windows.MessageBox.Show(string.Format("Unable to load the file '{0}'. Creating a new wix project!", fileName));
                    Console.WriteLine("Failed to load contents from the file!\n" + ex.Message + "\n" + ex.StackTrace);
                }
            }
            return wixDetails;
        }

        public void SaveToFile()
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Wix));
            XmlWriter writer = XmlWriter.Create(wxsFilepath.OriginalString);

            serializer.Serialize(writer, this);

            writer.Flush();
            writer.Close();
        }

        #region Visual Elements to Wix
        private void AddFilesAndFolders(DirectoryItem targetDirectory)
        {
            Directory wixDir = this.Product.Directory;
            AddDirectoryDetails(targetDirectory, wixDir);

            this.Product.Directory.Id = "TARGETDIR";
            this.Product.Directory.Name = "SourceDir";
        }

        private void AddShortcuts(DirectoryItem shortcutsDirectory)
        {
            if (shortcutsDirectory.Items != null && shortcutsDirectory.Items.Count > 0)
            {
                Directory wixDir = new Directory();
                AddDirectoryDetails(shortcutsDirectory, wixDir);

                List<Directory> targetDirSubDirectories = this.Product.Directory.Directory1.ToList();
                targetDirSubDirectories.AddRange(wixDir.Directory1); //Merged the shortcut directories under target dir.

                this.Product.Directory.Directory1 = targetDirSubDirectories.ToArray();
            }
        }

        private void AddDirectoryDetails(DirectoryItem directory, Directory wixDir)
        {
            wixDir.Id = directory.ID;
            wixDir.Name = directory.Name;

            List<Directory> subDirectories = new List<Directory>();


            foreach (Item nextDirectory in directory.Items)
            {
                if (nextDirectory is DirectoryItem)
                {
                    Directory subDirectory = new Directory();
                    AddDirectoryDetails((DirectoryItem)nextDirectory, subDirectory);
                    subDirectories.Add(subDirectory);
                    AddFiles((DirectoryItem)nextDirectory);
                }
            }

            wixDir.Directory1 = subDirectories.ToArray();
        }

        private void AddFiles(DirectoryItem directory)
        {
            bool hasFiles = false;
            bool hasShortcuts = false;

            foreach (Item nextFile in directory.Items)
            {
                if (nextFile is FileItem)
                {
                    hasFiles = true;
                    break;
                }

                if (nextFile is ShortcutItem)
                {
                    hasShortcuts = true;
                    break;
                }
            }

            if (hasFiles || hasShortcuts)
            {
                WixFragment fragment = new WixFragment();
                WixFragmentComponentGroup compGroup = new WixFragmentComponentGroup();
                fragment.ComponentGroup = compGroup;

                fragments.Add(fragment);

                List<WixFragmentComponentGroupComponent> components = new List<WixFragmentComponentGroupComponent>();

                foreach (Item nextItem in directory.Items)
                {
                    if (nextItem is FileItem)
                    {
                        components.Add(CreateFileComponent((FileItem)nextItem));
                    }
                    else if (nextItem is ShortcutItem)
                    {
                        components.Add(CreateShortcutComponent((ShortcutItem)nextItem));
                    }
                }

                compGroup.Component = components.ToArray();
                compGroup.Id = GenerateId("cmpg");
                compGroup.Directory = directory.ID;

                cmpGroupReferences.Add(new WixProductFeatureComponentGroupRef() { Id = compGroup.Id });
            }
        }

        private WixFragmentComponentGroupComponent CreateFileComponent(FileItem file)
        {
            WixFragmentComponentGroupComponent component = new WixFragmentComponentGroupComponent();
            component.Id = string.IsNullOrWhiteSpace(component.Id) ? GenerateId("cmp") : component.Id;
            component.Guid = string.IsNullOrWhiteSpace(file.Guid) ? Guid.NewGuid().ToString() : file.Guid;

            if (this.Product.Package.Platform.Equals("x64"))
            {
                component.Win64 = "yes";
            }
            else
            {
                component.Win64 = "no";
            }

            WixFragmentComponentGroupComponentFile componentFile = new WixFragmentComponentGroupComponentFile();
            component.File = componentFile;

            componentFile.Id = file.ID;
            componentFile.KeyPath = file.KeyPath ? "yes" : "no";
            componentFile.Name = file.Name;

            // TODO: Make relative URI wrt .wxs file
            if (System.IO.Path.IsPathRooted(file.Source))
            {
                Uri relativeUri = wxsFilepath.MakeRelativeUri(new Uri(file.Source));
                componentFile.Source = relativeUri.ToString().Replace("/", "\\");
            }
            else
            {
                componentFile.Source = file.Source;
            }

            return component;
        }

        private WixFragmentComponentGroupComponent CreateShortcutComponent(ShortcutItem shortcut)
        {
            WixFragmentComponentGroupComponent component = new WixFragmentComponentGroupComponent();
            component.Id = GenerateId("cmpt");
            component.Guid = string.IsNullOrWhiteSpace(shortcut.Guid) ? Guid.NewGuid().ToString() : shortcut.Guid;

            WixFragmentComponentGroupComponentShortcut componentShortcut = new WixFragmentComponentGroupComponentShortcut();
            componentShortcut.Id = shortcut.ID;
            componentShortcut.Name = shortcut.Name;
            componentShortcut.Description = shortcut.Description;
            componentShortcut.Target = string.Format("[{0}]{1}", shortcut.TargetDirectory, shortcut.TargetFile);
            componentShortcut.WorkingDirectory = shortcut.TargetDirectory;

            WixFragmentComponentGroupComponentRemoveFolder removeFolder = new WixFragmentComponentGroupComponentRemoveFolder();
            removeFolder.Id = GenerateId("rmfr");
            removeFolder.On = "uninstall";

            WixFragmentComponentGroupComponentRegistryValue regValue = new WixFragmentComponentGroupComponentRegistryValue();
            regValue.Root = "HKCU";
            regValue.Key = string.Format(@"Software\{0}\{1}", this.productField.Manufacturer, this.productField.Name);
            regValue.Name = "installed";
            regValue.Type = "integer";
            regValue.Value = "1";
            regValue.KeyPath = "yes";


            component.Shortcut = componentShortcut;
            component.RemoveFolder = removeFolder;
            component.RegistryValue = regValue;

            return component;
        }
        #endregion

        #region Wix to Visual Elements
        private WixFragmentComponentGroup GetComponentGroup(string directoryId)
        {
            WixFragmentComponentGroup componentGroup = null;
            foreach (WixFragment nextFragment in this.Fragment)
            {
                if (nextFragment.ComponentGroup.Directory == directoryId)
                {
                    componentGroup = nextFragment.ComponentGroup;
                    break;
                }
            }

            return componentGroup;
        }

        private DirectoryItem GetDirectoryItems()
        {
            DirectoryItem directory = new DirectoryItem();

            CreatDirectoryItem(this.Product.Directory, directory);

            return directory;
        }

        private void CreatDirectoryItem(Directory wixDir, DirectoryItem directory)
        {
            directory.ID = wixDir.Id;
            directory.Name = wixDir.Name;
            directory.SetDefaultsById();

            if (wixDir.Directory1 != null && wixDir.Directory1.Length > 0)
            {
                List<DirectoryItem> subDirectories = new List<DirectoryItem>();

                foreach (Directory nextDirectory in wixDir.Directory1)
                {
                    DirectoryItem subDirectory = new DirectoryItem();
                    CreatDirectoryItem(nextDirectory, subDirectory);
                    subDirectory.ParentItem = directory;
                    subDirectories.Add(subDirectory);
                    CreateFilesAndShortcuts(subDirectory);
                    if (subDirectory.HierarchyType == HierarchyType.ShortcutFolder && directory.HierarchyType != HierarchyType.RootFolder)
                    {
                        directory.HierarchyType = directory.HierarchyType == HierarchyType.SystemFolder ?
                                                    HierarchyType.SystemShortcutFolder : HierarchyType.ShortcutFolder;
                    }
                }

                directory.Items = new System.Collections.ObjectModel.ObservableCollection<Item>(subDirectories);
            }
        }

        private void CreateFilesAndShortcuts(DirectoryItem directory)
        {
            WixFragmentComponentGroup componentGroup = GetComponentGroup(directory.ID);

            if (componentGroup != null && componentGroup.Component != null && componentGroup.Component.Length > 0)
            {
                foreach (WixFragmentComponentGroupComponent nextComponent in componentGroup.Component)
                {
                    Item itemToAdd = null;

                    if (nextComponent.File != null)
                    {
                        WixFragmentComponentGroupComponentFile componentFile = nextComponent.File;

                        FileItem file = new FileItem();
                        file.ID = componentFile.Id;
                        file.KeyPath = componentFile.KeyPath == "yes" ? true : false;
                        file.Name = componentFile.Name;
                        file.Source = componentFile.Source;
                        file.Guid = nextComponent.Guid;

                        itemToAdd = file;
                    }
                    else if (nextComponent.Shortcut != null)
                    {
                        WixFragmentComponentGroupComponentShortcut shortcutComponent = nextComponent.Shortcut;
                        ShortcutItem shortcut = new ShortcutItem();

                        shortcut.ID = shortcutComponent.Id;
                        shortcut.Name = shortcutComponent.Name;
                        shortcut.Description = shortcutComponent.Description;
                        shortcut.TargetFile = shortcutComponent.Target.Substring(shortcutComponent.Target.IndexOf(']') + 1);
                        shortcut.TargetDirectory = shortcutComponent.WorkingDirectory;
                        shortcut.Guid = nextComponent.Guid;

                        itemToAdd = shortcut;

                        directory.HierarchyType = directory.HierarchyType == HierarchyType.SystemFolder ? HierarchyType.SystemShortcutFolder : HierarchyType.ShortcutFolder;
                    }

                    if (itemToAdd != null)
                    {
                        itemToAdd.IsContextMenuEnabled = true;
                        itemToAdd.ParentItem = directory;
                        directory.Items.Add(itemToAdd);
                    }
                }
            }
        }
        #endregion


        #region ID Generators
        public static string GenerateId(string prefix)
        {
            string id = Guid.NewGuid().ToString().Replace("-", string.Empty).ToLower();

            return prefix + id;
        }

        public static string GetFileId()
        {
            string id = Guid.NewGuid().ToString().Replace("-", string.Empty).ToLower();

            return "file" + id;
        }

        public static string GetShortcutId()
        {
            string id = Guid.NewGuid().ToString().Replace("-", string.Empty).ToLower();

            return "shrt" + id;
        }

        public static string GetDirectoryId()
        {
            string id = Guid.NewGuid().ToString().Replace("-", string.Empty).ToLower();

            return "dirc" + id;
        }
        #endregion


    }
}
